﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

namespace CSharp.Infrastructure
{
    public class AdbShellHelper
    {
        private string adb_path = null;

        public AdbShellHelper(string adb_path)
        {
            this.adb_path = adb_path;
        }

        public AdbShellHelper()
        {
            this.adb_path = ConfigurationManager.AppSettings["adb_path"];
        }

        /// <summary>
        /// adb device，列出设备集合
        /// </summary>
        /// <returns></returns>
        public string device()
        {
            return ReadProcess(adb_path, null, "devices");
        }

        /// <summary>
        /// adb 连接到设备， 一般情况是ip:端口
        /// </summary>
        /// <param name="device_name"></param>
        /// <returns></returns>
        public string connect(string device_name)
        {
            return ReadProcess(adb_path, null, "connect " + device_name);
        }

        /// <summary>
        /// adb 断开连接到设备， 一般情况是ip:端口
        /// </summary>
        /// <param name="device_name"></param>
        /// <returns></returns>
        public string dis_connect(string device_name)
        {
            return ReadProcess(adb_path, null, "disconnect " + device_name);
        }

        /// <summary>
        /// adb 点击指定设备，设备名称，x坐标，y坐标：执行adb shell input tap
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public string click(string device_name, int x, int y)
        {
            string cmd = string.Format("shell input tap {0} {1}", x, y);

            return ReadProcess(adb_path, device_name, cmd);
        }

        /// <summary>
        /// adb 长点击，用于弹窗，粘贴等操作
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public string long_click(string device_name, int x, int y)
        {
            // 282,174
            List<string> writeList = new List<string>();
            writeList.Add("sendevent /dev/input/event6 3 57 4294967295");
            writeList.Add("sendevent /dev/input/event6 0 0 0");
            writeList.Add("sendevent /dev/input/event6 3 47 0");
            writeList.Add("sendevent /dev/input/event6 3 54 240");
            writeList.Add("sendevent /dev/input/event6 3 48 12");
            writeList.Add("sendevent /dev/input/event6 3 57 0");
            List<string> writeList2 = new List<string>();
            writeList2.Add("sendevent /dev/input/event6 0 0 0");
            writeList2.Add("sendevent /dev/input/event6 3 54 481");
            writeList2.Add("sendevent /dev/input/event6 3 48 0");
            writeList2.Add("sendevent /dev/input/event6 3 57 4294967295");

            writeList2.Add("sendevent /dev/input/event6 0 0 0");
            writeList2.Add("sendevent /dev/input/event6 3 47 12");
            writeList2.Add("sendevent /dev/input/event6 3 57 12");
            writeList2.Add("sendevent /dev/input/event6 0 0 0");

            string cmd = string.Format("shell input swipe {0} {1} {0} {1} 2000", x, y);

            return ReadProcess(adb_path, device_name, cmd);
        }

        /// <summary>
        /// adb 发送点击事件
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public string send_click_event(string device_name, int x, int y)
        {
            // 282,174
            List<string> writeList = new List<string>();
            writeList.Add("sendevent /dev/input/event6 3 57 4294967295");
            writeList.Add("sendevent /dev/input/event6 0 0 0");
            writeList.Add("sendevent /dev/input/event6 3 47 0");
            writeList.Add("sendevent /dev/input/event6 3 54 240");
            writeList.Add("sendevent /dev/input/event6 3 48 12");
            writeList.Add("sendevent /dev/input/event6 3 57 0");
            List<string> writeList2 = new List<string>();
            writeList2.Add("sendevent /dev/input/event6 0 0 0");
            writeList2.Add("sendevent /dev/input/event6 3 54 481");
            writeList2.Add("sendevent /dev/input/event6 3 48 0");
            writeList2.Add("sendevent /dev/input/event6 3 57 4294967295");

            writeList2.Add("sendevent /dev/input/event6 0 0 0");
            writeList2.Add("sendevent /dev/input/event6 3 47 12");
            writeList2.Add("sendevent /dev/input/event6 3 57 12");
            writeList2.Add("sendevent /dev/input/event6 0 0 0");

            string cmd = "shell";

            return ReadProcess(adb_path, device_name, cmd, writeList, 2000, writeList2);
        }

        /// <summary>
        /// adb 输入操作，指定设备，设备名称，输入字符串：执行adb shell input text
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="text"></param>
        /// <returns></returns>
        public string input(string device_name, string text)
        {
            string cmd = string.Format("shell input text \"{0}\"", text);

            return ReadProcess(adb_path, device_name, cmd);
        }

        /// <summary>
        /// adb 执行启动指定apk，指定设备，设备名称，包名，启动类名：执行adb shell am start -n 
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="package"></param>
        /// <param name="activity_name"></param>
        /// <returns></returns>
        public string start(string device_name, string package, string activity_name)
        {
            string cmd = string.Format("shell \"am start -n {0}/{1}\"", package, activity_name);
            string str = ReadProcess(adb_path, device_name, cmd);

            if (str.Contains("Activity not started"))
            {
                Thread.Sleep(1000);
                str = ReadProcess(adb_path, device_name, cmd);
            }

            return str;
        }

        /// <summary>
        /// adb 执行启动指定apk，指定设备，设备名称，包名，启动类名，参数字符串(key=text)：执行adb shell am start -n 
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="package"></param>
        /// <param name="activity_name"></param>
        /// <param name="text"></param>
        /// <returns></returns>
        public string start_args(string device_name, string package, string activity_name, string text)
        {
            string cmd = string.Format("shell \"am start -n {0}/{1}\" --es text \"{2}\"", package, activity_name, text);
            string str = ReadProcess(adb_path, device_name, cmd);

            if (str.Contains("Activity not started"))
            {
                Thread.Sleep(1000);
                str = ReadProcess(adb_path, device_name, cmd);
            }

            return str;
        }

        /// <summary>
        /// adb 执行结束指定apk，指定设备，包名：执行adb shell am force-stop 
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="package"></param>
        /// <returns></returns>
        public string stop(string device_name, string package)
        {
            string cmd = string.Format("shell am force-stop {0}", package);
            string str = ReadProcess(adb_path, device_name, cmd);

            return str;
        }

        /// <summary>
        /// adb 执行自定义命令 指定设备，包名：执行 shell 命令, shell内容默认不含 shell 
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="cmd"></param>
        /// <returns></returns>
        public string shell(string device_name, string cmd)
        {
            return ReadProcess(adb_path, device_name, cmd);
        }

        /// <summary>
        /// adb 执行自定义命令 读取error数据 指定设备，包名：执行 shell 命令, shell内容默认不含 shell 
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="cmd"></param>
        /// <returns></returns>
        public string shell_error(string device_name, string cmd)
        {
            return ReadProcess_Error(adb_path, device_name, cmd);
        }

        /// <summary>
        /// adb 调用keyevent执行操作 指定设备，event_code：执行 input keyevent 命令 
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="event_code"></param>
        /// <returns></returns>
        public string key_event(string device_name, int event_code)
        {
            string cmd = string.Format("shell input keyevent {0}", event_code);

            return ReadProcess(adb_path, device_name, cmd);
        }

        /// <summary>
        /// adb 调用cat执行操作 指定设备，文件路径：执行 cat 文件路径 
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="file_path"></param>
        /// <returns></returns>
        public string read_file(string device_name, string file_path)
        {
            string str = string.Empty;
            string cmd = string.Format("shell cat {0}", file_path);
            try
            {
                str = ReadProcess(adb_path, device_name, cmd);
            }
            catch (Exception e)
            {
                str = e.Message;
            }

            return str;
        }

        /// <summary>
        /// adb 调用删除文件操作 指定设备，文件路径：执行 rm -r 文件路径 
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="file_path"></param>
        /// <returns></returns>
        public string delete_file(string device_name, string file_path)
        {
            string cmd = string.Format("shell rm -r {0}", file_path);

            return ReadProcess(adb_path, device_name, cmd);
        }

        /// <summary>
        /// adb 调用删除多个文件操作 指定设备，文件夹路径，文件路径逗号分隔：执行 rm -r 文件路径 
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="root_dir"></param>
        /// <returns></returns>
        public string[] list_dir(string device_name, string root_dir)
        {
            string cmd = string.Format("shell ls {0}", root_dir);

            string result = ReadProcess(adb_path, device_name, cmd);

            return result.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).ToArray();
        }

        /// <summary>
        /// adb 调用删除多个文件操作 指定设备，文件夹路径，文件路径逗号分隔：执行 rm -r 文件路径 
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="root_dir"></param>
        /// <param name="dir_paths"></param>
        public void delete_dir(string device_name, string root_dir, string dir_paths)
        {
            string[] dirArray = dir_paths.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var dir in dirArray)
            {
                string cmd = string.Format("shell rm -r {0}", root_dir + "/" + dir);

                ReadProcess(adb_path, device_name, cmd);
            }
        }

        /// <summary>
        /// adb 截图 保存到模拟器指定位置 指定设备，图片路径（模拟器）：shell /system/bin/screencap -p 文件路径 
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="image_path"></param>
        public void cut_image(string device_name, string image_path)
        {
            string guid = Guid.NewGuid().ToString("N");
            ReadProcess(adb_path, device_name, "shell /system/bin/screencap -p " + image_path);

            Thread.Sleep(1000);
        }

        /// <summary>
        /// adb 截图 并且拷贝图片到PC下 指定设备：执行命令1 shell /system/bin/screencap -p 文件路径 
        /// 执行命令2 pull 图片路径 PC路径 返回图片PC路径
        /// </summary>
        /// <param name="device_name"></param>
        /// <returns></returns>
        public string cut_image_to_local(string device_name, string default_dir)
        {
            string file_ext = ".png";
            if (!Directory.Exists(default_dir))
            {
                Directory.CreateDirectory(default_dir);
            }

            // string save_path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tmp", Guid.NewGuid().ToString("N") + file_ext);
            string save_path = Path.Combine(default_dir, Guid.NewGuid().ToString("N") + file_ext);
            if (!File.Exists(save_path))
            {
                using (File.Create(save_path)) ;
            }

            string guid = Guid.NewGuid().ToString("N");
            ReadProcess(adb_path, device_name, "shell /system/bin/screencap -p /sdcard/" + guid + file_ext);
            ReadProcess(adb_path, device_name, "pull /sdcard/" + guid + file_ext + " \"" + save_path + "\"");
            Thread.Sleep(1000);
            if (!File.Exists(save_path))
            {
                Thread.Sleep(1000);
            }

            delete_sdcard_pic_delay(device_name, "/sdcard/" + guid + file_ext);
            return save_path;
        }

        /// <summary>
        /// adb 截图 并且保存到模拟器中指定文件夹下 指定设备，指定文件夹：执行命令1 shell /system/bin/screencap -p 文件路径
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="android_dir"></param>
        /// <returns></returns>
        public string cut_image_random(string device_name, string android_dir)
        {
            ReadProcess(adb_path, device_name, "shell /system/bin/screencap -p " + android_dir + "/" + Guid.NewGuid().ToString("N"));

            return android_dir + "/" + Guid.NewGuid().ToString("N");
        }

        /// <summary>
        /// 从模拟器到PC
        /// adb pull 拷贝从模拟器到PC上 指定设备，模拟器路径，PC路径：执行命令1 shell pull 路径1 路径2
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="path1"></param>
        /// <param name="path2"></param>
        /// <returns></returns>
        public string pull(string device_name, string path1, string path2)
        {
            return ReadProcess(adb_path, device_name, "pull \"" + path1 + "\" \"" + path2 + "\"");
        }

        /// <summary>
        /// 从PC到模拟器
        /// adb push 拷贝从PC到模拟器上 指定设备，PC路径，模拟器路径：执行命令1 shell push 路径1 路径2
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="path1"></param>
        /// <param name="path2"></param>
        /// <returns></returns>
        public string push(string device_name, string path1, string path2)
        {
            return ReadProcess(adb_path, device_name, "push \"" + path1 + "\" \"" + path2 + "\"");
        }

        /// <summary>
        /// adb 指定多个文件夹压缩到一个tar文件中 指定设备，文件夹上级路径，子文件夹名称多个都好分割, 压缩后的文件名：执行命令1 shell tar -cvf zip文件路径 多个子文件夹路径
        /// </summary>
        /// <param name="device_name"></param>
        /// <param name="dir"></param>
        /// <param name="childDirNames"></param>
        /// <param name="zip_path"></param>
        public void dir_to_zip(string device_name, string dir, string childDirNames, string zip_path)
        {
            ReadProcess(adb_path, device_name, "shell tar -cvf " + zip_path + " " + string.Join(" ", dir.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)));
        }

        private void delete_sdcard_pic_delay(string device_name, string file_path, int delay_time = 1000)
        {
            // 延时删除图片
            new Action(() =>
            {
                Thread.Sleep(delay_time);
                ReadProcess(adb_path, device_name, "shell rm " + file_path);
            }).BeginInvoke(null, null);
        }

        private static readonly object obj = new object();

        public static string ReadProcess(string adbpath, string clientname, string args)
        {
            using (Process process = new Process())
            {
                process.StartInfo.FileName = adbpath;

                string fileCmd = string.IsNullOrEmpty(clientname) ? "" : (" -s " + clientname + " ");
                process.StartInfo.Arguments = (args == null ? string.Empty : (fileCmd + args));
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.Start();

                process.WaitForExit();
                var result = process.StandardOutput.ReadToEnd();

                return result;
            }
        }

        public static string ReadProcess_Error(string adbpath, string clientname, string args)
        {
            using (Process process = new Process())
            {
                process.StartInfo.FileName = adbpath;

                string fileCmd = string.IsNullOrEmpty(clientname) ? "" : (" -s " + clientname + " ");
                process.StartInfo.Arguments = (args == null ? string.Empty : (fileCmd + args));
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.RedirectStandardError = true;
                process.Start();

                process.WaitForExit();
                var result = process.StandardError.ReadToEnd();

                return result;
            }
        }

        public static string ReadProcess(string adbpath, string clientname, string args, List<string> writeList, int sleep, List<string> writeList2)
        {
            using (Process process = new Process())
            {
                process.StartInfo.FileName = adbpath;

                string fileCmd = string.IsNullOrEmpty(clientname) ? "" : (" -s " + clientname + " ");
                process.StartInfo.Arguments = (args == null ? string.Empty : (fileCmd + args));
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardInput = true;
                process.Start();
                foreach (var str in writeList)
                {
                    process.StandardInput.WriteLine(str);
                }

                Thread.Sleep(sleep);
                foreach (var str in writeList2)
                {
                    process.StandardInput.WriteLine(str);
                }

                process.WaitForExit();
                var result = process.StandardOutput.ReadToEnd();

                return result;
            }
        }
    }
}
